import {
  Injectable,
  Inject,
  forwardRef,
  BadRequestException,
} from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model, Types } from 'mongoose';
import { DocFrameType, LcDocument, LcProject } from '../../types/editor.type';
import { ITypeEntity } from '../../../superType/types';
import { validateAndUpdateType } from '../virtual-file-system/adapters/pipe/validateAndUpdateType';
import {
  getDocumentPath,
  getTypePath,
} from '../virtual-file-system/utils/source-path';
import { getFileInfoFromPath } from '../virtual-file-system/utils/getFileInfoFromPath';
import { generateDocumentCode } from '../code-transformer/schemaToCode/fileGen';
import { getTypeFileCode } from '../virtual-file-system/adapters/pipe/getFileCode';
import { InitializationResult, FileSystemFile } from './dto/vfs.dto';
import { FileSystemEntry, FileSystemStats } from '../virtual-file-system/types';
import { IPublicTypeRootSchema } from '@alilc/lowcode-types';
import { TSXComplianceChecker } from '../validation/TSXComplianceChecker';
import { validateAndUpdateDocument } from '../virtual-file-system/adapters/pipe/validateAndUpdateDocument';
import {
  calculateDocumentHashes,
  calculateHash,
  calculateProjectHashes,
  calculateTypeEntityHashes,
  getProjectFile,
  PROJECT_CONFIG_FIELDS,
} from '../virtual-file-system/utils/calculateHashs';
import { EditorService } from '../editor.service';
import { VFSEventService } from './vfs-event.service';
import { FileSystemEntryBuilder } from './file-system-entry-builder.service';
import { ReadOptions, WriteOptions, ReadResult, FileWriteResult } from './type';
import { parsingCss } from '../virtual-file-system/adapters/css-transform/parsingCss';
import { TypeService } from '../../../superType/server/type.service';

@Injectable()
export class VFSService {
  constructor(
    @InjectModel('project') private projectModel: Model<LcProject>,
    @InjectModel('doc') private documentModel: Model<LcDocument>,
    @InjectModel('type') private typeModel: Model<ITypeEntity>,
    @Inject(forwardRef(() => EditorService))
    private editorService: EditorService,
    @Inject(forwardRef(() => VFSEventService))
    private vfsEventService: VFSEventService,
    private fileSystemEntryBuilder: FileSystemEntryBuilder,
    @Inject(forwardRef(() => TypeService))
    private typeService: TypeService,
  ) {}

  /**
   * 读取文件内容
   */
  async readFile(
    projectId: string,
    path: string,
    options: ReadOptions = {},
  ): Promise<ReadResult> {
    let content = null;
    try {
      let source;
      let entry;
      const { returnSource = false } = options;

      if (path === '/src/project.json') {
        const project = await this.getProjectEntity(projectId);
        content = await getProjectFile(project);
        entry = this.fileSystemEntryBuilder.buildFromProject(project);
        if (returnSource) {
          source = project;
        }
      } else if (path === '/src/app.css') {
        const project = await this.getProjectEntity(projectId);
        const schema =
          typeof project.schema === 'string'
            ? JSON.parse(project.schema)
            : project.schema;
        content = schema.css;
        entry = this.fileSystemEntryBuilder.buildCssFromProject(project);
        if (returnSource) {
          source = project;
        }
      } else {
        // 解析文件路径，确定文件类型
        const fileInfo = getFileInfoFromPath(path, undefined, projectId);

        console.log('fileInfo: ', fileInfo);

        if (
          fileInfo.type === 'Page' ||
          fileInfo.type === 'Component' ||
          fileInfo.type === 'css'
        ) {
          // 处理文档文件 (.doc/.tsx)
          const { code, document } = await this.readDocumentFile(
            projectId,
            fileInfo,
            {
              documentSchema: options.documentSchema,
            },
          );
          console.log('code: ', code);
          content = code;
          entry = this.fileSystemEntryBuilder.buildFromDocument(document);
          if (returnSource) {
            source = document;
          }
        } else {
          // 处理类型文件
          const { code, typeEntity } = await this.readTypeFile(
            projectId,
            fileInfo,
            options,
          );
          content = code;
          entry = this.fileSystemEntryBuilder.buildFromTypeEntity(typeEntity);
          if (returnSource) {
            source = typeEntity;
          }
        }
      }
      return {
        path,
        content,
        timestamp: Date.now(),
        entry,
        source,
      };
    } catch (error) {
      console.error(error);
      throw new Error(`读取文件失败 ${path}: ${error.message}`);
    }
  }

  /**
   * 写入文件内容
   */
  async writeFile(
    projectId: string,
    path: string,
    content: string,
    options: WriteOptions = {},
  ): Promise<FileWriteResult> {
    try {
      const { writeMode = 'code', author, message, userId } = options;

      const fileInfo = getFileInfoFromPath(path);
      console.log('fileInfo: ', fileInfo);

      if (
        path.endsWith('.tsx') ||
        (path.endsWith('.css') && path !== '/src/app.css')
      ) {
        // 处理文档文件写入
        return await this.writeDocumentFile(
          projectId,
          path,
          fileInfo.name,
          content,
          options,
        );
      } else if (
        (path.endsWith('.css') && path === '/src/app.css') ||
        path === '/src/project.json'
      ) {
        return await this.writeProjectFile(projectId, path, content, options);
      } else {
        // 处理类型文件写入
        return await this.writeTypeFile(projectId, fileInfo, content, options);
      }
    } catch (error) {
      throw new Error(`写入文件失败 ${path}: ${error.message}`);
    }
  }

  /**
   * 删除文件
   */
  async deleteFile(
    projectId: string,
    path: string,
    userId: number,
  ): Promise<void> {
    const fileInfo = getFileInfoFromPath(path);

    if (fileInfo.type === 'document') {
      await this.deleteDocumentFile(projectId, fileInfo.name);
    } else if (fileInfo.type === 'type') {
      await this.deleteTypeFile(projectId, fileInfo);
    }

    // 记录删除操作到历史
    await this.recordHistory(projectId, path, '', 'delete', userId);
  }

  /**
   * 列出目录内容
   */
  async readdir(
    projectId: string,
    path: string,
    options: { recursive?: boolean; includeTypeEntity?: boolean } = {},
  ): Promise<Array<FileSystemEntry>> {
    try {
      const entries: Array<FileSystemEntry> = [];

      const project = await this.projectModel
        .findById({
          _id: new Types.ObjectId(projectId),
        })
        .select(
          'path hash cssPath cssHash cssUpdateAt create_time update_time lastUpdateBy title description width height screenshot frames',
        );

      // 使用统一构建器构建 Project 主文件
      entries.push(this.fileSystemEntryBuilder.buildFromProject(project));

      // 使用统一构建器构建 Project CSS 文件
      const projectCssEntry =
        this.fileSystemEntryBuilder.buildCssFromProject(project);
      if (projectCssEntry) {
        entries.push(projectCssEntry);
      }

      // 获取文档列表
      const documents = await this.documentModel
        .find({
          projectId: new Types.ObjectId(projectId),
        })
        .select(
          'fileName portal create_time update_time lastUpdateBy title description width height screenshot folder frames route type path hash cssHash cssPath cssUpdateAt',
        );

      // 使用统一构建器添加文档到列表
      documents.forEach((doc) => {
        // 构建主文件
        entries.push(this.fileSystemEntryBuilder.buildFromDocument(doc));

        // 构建 CSS 文件
        const cssEntry = this.fileSystemEntryBuilder.buildCssFromDocument(doc);
        if (cssEntry) {
          entries.push(cssEntry);
        }
      });

      // 获取类型实体列表
      const types = await this.typeModel
        .find({
          projectId: new Types.ObjectId(projectId),
        })
        .select(
          'name desc intro type uri method contentType db path create_time update_time routePath noLayout userId size hash',
        );

      // 使用统一构建器添加类型实体到列表
      types.forEach((type) => {
        entries.push(this.fileSystemEntryBuilder.buildFromTypeEntity(type));
      });
      console.log(entries);
      return entries
        .filter((_) => !!_.path)
        .sort((a, b) => a.path.localeCompare(b.path));
    } catch (error) {
      console.error(error);
      throw new Error(`读取目录失败 ${path}: ${error.message}`);
    }
  }

  /**
   * 检查用户权限
   */
  async checkPermission(
    userId: number,
    projectId: string,
    path: string,
    action: 'read' | 'write' | 'delete',
  ): Promise<boolean> {
    // 这里需要实现权限检查逻辑
    // 暂时返回true，实际应用中需要查询用户权限
    return true;
  }

  validateFile(
    path: string,
    content: string,
  ): { isCompliant: boolean; issues: any[] } {
    if (path.endsWith('.tsx')) {
      return TSXComplianceChecker.checkTSXCompliance(content, path);
    }
    return { isCompliant: true, issues: [] };
  }

  /**
   * 私有方法：读取文档文件
   */
  private async readDocumentFile(
    projectId: string,
    fileInfo: { path: string; type: string },
    options: ReadOptions,
  ): Promise<{
    document: LcDocument;
    code: string;
  }> {
    const document = await this.getDocument(
      projectId,
      fileInfo.path.replace('.css', '.tsx'),
    );

    console.log('document: ', document);

    if (!document) {
      throw new Error(`文档不存在: ${fileInfo.path}`);
    }
    const schema =
      options.documentSchema ||
      (typeof document.schema === 'string'
        ? JSON.parse(document.schema)
        : document.schema);

    if (fileInfo.type === 'css') {
      // if (!schema.css) {
      //   throw new Error(`文档不存在: ${fileInfo.path}`);
      // }
      return {
        code: schema.css || '',
        document,
      };
    }
    document.schema = schema;

    const code = await generateDocumentCode(document, {
      path: fileInfo.path,
      generateRender: true,
      currentFrame: options.currentFrame ? 'web' : 'app',
    });
    return { document, code };
  }

  /**
   * 私有方法：写入文档文件
   */
  private async writeDocumentFile(
    projectId: string,
    path,
    fileName: string,
    content: string,
    options: WriteOptions,
  ): Promise<FileWriteResult> {
    const isCss = path.endsWith('.css');
    const existingDoc = await this.getDocument(
      projectId,
      isCss ? path.replace('.css', '.tsx') : path,
    );
    let isNewFile = !existingDoc;
    let isChanged = false;
    let updatedDoc;
    if (isCss) {
      if (existingDoc) {
        const schema =
          typeof existingDoc.schema === 'string'
            ? JSON.parse(existingDoc.schema)
            : existingDoc.schema;
        schema.css = content;
        const newHash = calculateHash(content);
        if (!schema.css) {
          isNewFile = true;
        }
        console.log('schema: ', schema.css);
        updatedDoc = existingDoc;
        console.log('newHash: ', newHash);
        console.log('existingDoc.cssHash: ', existingDoc.cssHash);
        if (newHash !== existingDoc.cssHash) {
          updatedDoc.schema = schema;
          updatedDoc.cssHash = newHash;
          updatedDoc.cssPath = path;
          updatedDoc.cssUpdateAt = Date.now();
          isChanged = true;
        } else {
          return {
            hash: existingDoc.cssHash,
            isNewFile,
            isChanged,
          };
        }
      } else {
        // @TODO: 这里需要处理组件还未创建的情况,后续可预先创建空组件
        throw new BadRequestException('组件还未创建，不能先写入CSS');
      }
    } else {
      const { doc } = await validateAndUpdateDocument(
        projectId,
        path,
        content,
        {
          schema: options.documentSchema,
          existDocument: existingDoc,
        },
      );
      updatedDoc = doc;
    }

    const willSavedDoc: Partial<LcDocument> = {
      _id: existingDoc?._id,
      ...updatedDoc,
      projectId,
      fileName,
    };

    console.log('options: ', options);

    if (options.preview) {
      const hash = calculateHash(content);
      console.log('willSavedDoc: ', willSavedDoc);
      return {
        hash,
        doc: willSavedDoc,
        isNewFile,
        isChanged,
      };
    }

    // 调用 EditorService 的 saveDocument 方法保存文档
    const savedDoc = await this.editorService.saveDocument(willSavedDoc, {
      userId: options.userId || 0,
      existingDoc,
      skipValidation: options.skipValidation,
      skipPermissionCheck: true, // VFS 层面已经做了权限检查
    });

    // 计算 hash 用于返回
    const hash = savedDoc.hash || existingDoc?.hash;
    if (!isCss) {
      isChanged = existingDoc?.hash !== hash;
    }

    // // 推送文件变更事件
    // if (isNewFile) {
    //   await this.vfsEventService.emitFileCreated(
    //     projectId,
    //     path,
    //     entry,
    //     options.userId,
    //   );
    // } else if (isChanged) {
    //   await this.vfsEventService.emitFileUpdated(
    //     projectId,
    //     path,
    //     entry,
    //     options.userId,
    //   );
    // }

    console.log('isChanged: ', isChanged);

    if (options.returnSource) {
      return {
        hash,
        isNewFile,
        isChanged,
        doc: savedDoc,
        entry: isCss
          ? this.fileSystemEntryBuilder.buildCssFromDocument(savedDoc)
          : this.fileSystemEntryBuilder.buildFromDocument(savedDoc),
      };
    }
    return {
      hash,
      isNewFile,
      isChanged,
    };
  }

  /**
   * 私有方法：获取项目
   */
  private async getProject(projectId: string): Promise<LcProject | null> {
    return await this.projectModel.findById(projectId).lean();
  }

  /**
   * 私有方法：获取文档
   */
  private async getDocument(
    projectId: string,
    path: string,
  ): Promise<LcDocument | null> {
    return this.documentModel
      .findOne({
        projectId: new Types.ObjectId(projectId),
        path,
      })
      .lean();
  }

  /**
   * 私有方法：获取项目实体
   */
  private async getProjectEntity(projectId: string): Promise<LcProject | null> {
    return this.projectModel
      .findOne({
        _id: new Types.ObjectId(projectId),
      })
      .lean();
  }

  /**
   * 私有方法：获取类型实体
   */
  private async getTypeEntity(
    projectId: string,
    fileInfo: any,
  ): Promise<ITypeEntity | null> {
    return this.typeModel
      .findOne({
        projectId: new Types.ObjectId(projectId),
        path: fileInfo.path,
      })
      .lean();
  }

  /**
   * 私有方法：删除文档文件
   */
  private async deleteDocumentFile(
    projectId: string,
    fileName: string,
  ): Promise<void> {
    await this.documentModel.deleteOne({
      projectId: new Types.ObjectId(projectId),
      fileName,
    });
  }

  /**
   * 私有方法：删除类型文件
   */
  private async deleteTypeFile(
    projectId: string,
    fileInfo: any,
  ): Promise<void> {
    await this.typeModel.deleteOne({
      projectId: new Types.ObjectId(projectId),
      name: fileInfo.fileName,
      type: fileInfo.category || 'data',
    });
  }

  private async writeProjectFile(
    projectId: string,
    path: string,
    content: string,
    options: WriteOptions,
  ): Promise<FileWriteResult> {
    // 获取现有项目
    const existingProject = await this.getProject(projectId);

    if (!existingProject) {
      throw new BadRequestException('项目不存在，无法写入项目文件');
    }

    if (path === '/src/app.css') {
      // 处理 CSS 文件
      return await this.writeProjectCssContent(
        projectId,
        path,
        content,
        options,
        existingProject,
      );
    } else if (path === '/src/project.json') {
      // 处理项目配置文件
      return await this.writeProjectConfigContent(
        projectId,
        path,
        content,
        options,
        existingProject,
      );
    } else {
      throw new BadRequestException(`不支持的项目文件路径: ${path}`);
    }
  }

  private async writeProjectCssContent(
    projectId: string,
    path: string,
    content: string,
    options: WriteOptions,
    existingProject: LcProject,
  ): Promise<FileWriteResult> {
    let isNewFile = !existingProject.cssHash;
    let isChanged = false;

    // 解析现有的 schema
    const schema =
      typeof existingProject.schema === 'string'
        ? JSON.parse(existingProject.schema)
        : existingProject.schema || {};

    // 更新 CSS 内容
    schema.css = content;
    const cssList = parsingCss(content, {
      existCssList: (schema as any)?.cssList || [],
    });
    schema.cssList = cssList;
    const newHash = calculateHash(content);

    const updatedProject = existingProject;
    if (newHash !== existingProject.cssHash) {
      isChanged = true;
      updatedProject.cssHash = newHash;
      updatedProject.cssPath = path;
      updatedProject.cssUpdateAt = Date.now();
    } else {
      // 内容没有改变，直接返回
      return {
        hash: existingProject.cssHash,
        isNewFile,
        isChanged,
      };
    }

    // 准备要保存的项目数据
    const willSavedProject: Partial<LcProject> = {
      _id: existingProject._id,
      ...updatedProject,
      schemaPartial: {
        css: content,
        cssList,
      },
    };

    if (options.preview) {
      const hash = calculateHash(content);
      return {
        hash,
        project: willSavedProject,
        isNewFile,
        isChanged,
      };
    }

    // 调用 EditorService 的 updateProject 方法保存项目
    const savedProject = await this.editorService.updateProject(
      projectId,
      willSavedProject,
      options.user,
      {
        skipHashCalculation: true, // 已经手动计算过哈希
        existProject: existingProject,
      },
    );

    // 计算 hash 用于返回
    const hash = savedProject.cssHash || existingProject?.cssHash;

    if (options.returnSource) {
      return {
        hash,
        isNewFile,
        isChanged,
        project: savedProject,
        entry: this.fileSystemEntryBuilder.buildCssFromProject(savedProject),
      };
    }

    return {
      hash,
      isNewFile,
      isChanged,
    };
  }

  private async writeProjectConfigContent(
    projectId: string,
    path: string,
    content: string,
    options: WriteOptions,
    existingProject: LcProject,
  ): Promise<FileWriteResult> {
    let isChanged = false;

    try {
      // 解析传入的 JSON 内容
      const projectConfig: Partial<LcProject> = JSON.parse(content);

      // 使用统一的项目配置字段常量
      const updateData: any = {};
      for (const field of PROJECT_CONFIG_FIELDS) {
        if (projectConfig[field] !== undefined) {
          updateData[field] = projectConfig[field];
        }
      }

      // 生成新的项目配置内容并计算哈希
      const mergedProject = { ...existingProject, ...updateData };
      const newProjectContent = await getProjectFile(mergedProject);
      const newHash = calculateHash(newProjectContent);

      // 检查是否发生变化
      if (newHash !== existingProject.hash) {
        isChanged = true;
        updateData.hash = newHash;
        updateData.path = path;
      } else {
        // 内容没有改变，直接返回
        return {
          hash: existingProject.hash,
          isNewFile: false,
          isChanged,
        };
      }

      if (options.preview) {
        return {
          hash: newHash,
          project: { ...existingProject, ...updateData },
          isNewFile: false,
          isChanged,
        };
      }

      // 调用 EditorService 的 updateProject 方法保存项目
      const savedProject = await this.editorService.updateProject(
        projectId,
        updateData,
        options.user,
        {
          skipHashCalculation: true, // 已经手动计算过哈希
          existProject: existingProject,
        },
      );

      if (options.returnSource) {
        return {
          hash: newHash,
          isNewFile: false,
          isChanged,
          project: savedProject,
          entry: this.fileSystemEntryBuilder.buildFromProject(savedProject),
        };
      }

      return {
        hash: newHash,
        isNewFile: false,
        isChanged,
      };
    } catch (error) {
      throw new BadRequestException(`解析项目配置 JSON 失败: ${error.message}`);
    }
  }

  /**
   * 私有方法：记录历史操作
   */
  private async recordHistory(
    projectId: string,
    path: string,
    content: string,
    action: 'add' | 'modify' | 'delete',
    userId?: number,
  ): Promise<void> {
    // 这里可以实现历史记录逻辑
    // 比如保存到文件变更历史表中
    console.log(`记录历史操作: ${projectId} ${path} ${action} by ${userId}`);
  }

  /**
   * 私有方法：读取类型文件
   */
  private async readTypeFile(
    projectId: string,
    fileInfo: any,
    options: ReadOptions,
  ): Promise<{
    code: string;
    typeEntity: any;
  }> {
    const typeEntity =
      options.typeEntity || (await this.getTypeEntity(projectId, fileInfo));

    if (!typeEntity) {
      throw new Error(`类型实体不存在: ${fileInfo.fileName}`);
    }

    const code = await getTypeFileCode(typeEntity);
    return {
      code,
      typeEntity,
    };
  }

  /**
   * 私有方法：写入类型文件
   */
  private async writeTypeFile(
    projectId: string,
    fileInfo: any,
    content: string,
    options: WriteOptions,
  ): Promise<FileWriteResult> {
    // 获取现有类型实体
    const existTypeEntity = await this.getTypeEntity(projectId, fileInfo);
    const isNewFile = !existTypeEntity;

    // 使用改造后的 validateAndUpdateType 验证并处理类型文件
    const validationResult = await validateAndUpdateType(
      projectId,
      fileInfo.path,
      content,
      {
        existTypeEntity,
        ...options,
      },
    );

    console.log('validationResult: ', validationResult);

    // 检查验证结果
    if (
      validationResult.complianceResult &&
      !validationResult.complianceResult.isCompliant
    ) {
      throw new Error(
        `类型文件验证失败: ${validationResult.complianceResult.errors?.join(
          ', ',
        )}`,
      );
    }

    if (!validationResult.typeEntity) {
      throw new Error('类型文件处理失败');
    }

    const typeEntity = validationResult.typeEntity;

    // 确保必要的字段
    typeEntity.projectId = typeEntity.projectId || projectId;
    typeEntity.create_time = typeEntity.create_time || Date.now();
    typeEntity.update_time = Date.now();

    if (options.userId) {
      typeEntity.userId = options.userId;
    }


    try {
      // 🆕 使用改造后的 typeService.saveType
      const savedTypeEntity = await this.typeService.saveType(typeEntity, {
        userId: options.userId || 0,
        teamId: options.teamId || 0, // 需要从项目信息中获取
        skipValidation: options.skipValidation,
        skipPermissionCheck: true, // VFS 层已做权限检查
        existType: existTypeEntity,
      });
      const isChanged = savedTypeEntity.hash !== existTypeEntity.hash;

      if (options.returnSource) {
        return {
          hash: savedTypeEntity.hash,
          isNewFile,
          isChanged,
          typeEntity: savedTypeEntity,
        };
      }

      return {
        hash: savedTypeEntity.hash,
        isNewFile,
        isChanged,
      };
    } catch (error) {
      console.error('类型实体保存失败:', error);
      throw new Error(`类型实体保存失败: ${error.message}`);
    }
  }

  /**
   * 私有方法：写入其他文件
   */
  private async writeOtherFile(
    projectId: string,
    path: string,
    content: string,
  ): Promise<FileWriteResult> {
    // 这里可以实现写入其他类型文件的逻辑
    throw new Error(`暂不支持写入此类型文件: ${path}`);
  }

  /**
   * 初始化项目文件系统
   * @param projectId 项目ID
   * @param project 项目对象（包含 documents）
   * @returns 初始化结果
   */
  async initializeFileSystem(
    projectId: string,
    project: LcProject,
  ): Promise<InitializationResult> {
    const startTime = Date.now();
    const result: InitializationResult = {
      success: false,
      projectId,
      totalFiles: 0,
      generatedFiles: 0,
      updatedDocuments: 0,
      updatedTypes: 0,
      errors: [],
      duration: 0,
    };

    try {
      console.log(`开始初始化项目文件系统: ${projectId}`);

      // 1. 生成所有文件
      const project = await this.projectModel.findById(projectId).lean();

      // 2. 批量更新文档的 CSS 和哈希信息
      const documentUpdateResult = await this.updateDocumentsHashes(projectId);
      result.updatedDocuments = documentUpdateResult.updated;

      // 3. 批量更新类型实体的哈希信息
      // const typeUpdateResult = await this.updateTypesHashes(
      //   projectId,
      //   allFiles.filter(f => f.entityType === 'type')
      // );
      // result.updatedTypes = typeUpdateResult.updated;

      // 4. 构建文件系统结构（用于缓存）
      // const fileSystemStructure = this.buildFileSystemStructure(allFiles);

      // TODO: 5. 更新缓存（Redis）
      // await this.updateFileSystemCache(projectId, fileSystemStructure);

      result.success = true;
      result.duration = Date.now() - startTime;

      console.log(`文件系统初始化完成`, {
        projectId,
        duration: `${result.duration}ms`,
        totalFiles: result.totalFiles,
        updatedDocuments: result.updatedDocuments,
        updatedTypes: result.updatedTypes,
      });
    } catch (error) {
      result.errors.push(error.message);
      result.duration = Date.now() - startTime;
      console.error(`文件系统初始化失败`, error.stack);
    }

    return result;
  }

  /**
   * 批量更新项目哈希信息
   */
  async updateProjectHashes(
    projectId?: string,
  ): Promise<{ updated: number; errors: string[] }> {
    const errors: string[] = [];
    let updated = 0;
    let processed = 0; // 实际处理的文档数（有schema的）
    let skip = 0;
    const batchSize = 100;
    let hasMore = true;

    // 循环处理，每次100个
    while (hasMore) {
      try {
        // 查询 cssUpdateAt 不存在的文档，每次100个
        const projects = await this.projectModel
          .find(
            projectId
              ? {
                  _id: new Types.ObjectId(projectId),
                  // cssUpdateAt: { $exists: false },
                }
              : {},
          )
          .skip(skip)
          .limit(batchSize);

        if (projects.length === 0) {
          hasMore = false;
          break;
        }

        // 批量处理这批文档
        for (const project of projects) {
          try {
            if (!project._id) continue;

            processed++; // 统计实际处理的文档

            try {
              // 使用独立的hash计算方法
              const { path, hash, cssPath, cssHash, cleanedSchema } =
                await calculateProjectHashes(project);

              // 更新项目
              await this.projectModel.updateOne(
                { _id: project._id },
                {
                  $set: {
                    path,
                    hash,
                    cssPath,
                    cssHash: cssHash || null,
                    cssUpdateAt: Date.now(),
                  },
                },
              );

              updated++;

              // 每处理10个输出一次进度
              if (updated % 10 === 0) {
                console.log(`已更新项目 CSS: ${updated} 个`);
              }
            } catch (error) {
              console.error(`计算项目 ${project.title} 的哈希失败`, error);
              // 不要直接返回，继续处理其他项目
              errors.push(
                `计算项目 ${project.title} 的哈希失败: ${error.message}`,
              );
            }
          } catch (error) {
            errors.push(`更新项目 ${project.title} 失败: ${error.message}`);
            console.error(`更新项目 ${project.title} 失败`, error);
          }
        }

        skip += batchSize;
        console.log(
          `批次完成，已查询 ${skip} 个文档，实际处理 ${processed} 个，成功更新 ${updated} 个`,
        );
      } catch (error) {
        errors.push(`批次处理失败: ${error.message}`);
        console.error('批次处理失败', error);

        // 如果错误太多，中断处理
        if (errors.length > 10) {
          console.error('错误过多，中断批量更新');
          break;
        }
      }
    }

    console.log(
      `批量更新文档 CSS 完成: 总共查询 ${skip} 个文档，实际处理 ${processed} 个，成功更新 ${updated} 个，错误 ${errors.length} 个`,
    );
    return { updated, errors };
  }

  /**
   * 批量更新文档的 CSS 和哈希信息
   */
  async updateDocumentsHashes(
    projectId: string,
  ): Promise<{ updated: number; errors: string[] }> {
    const errors: string[] = [];
    let updated = 0;
    let processed = 0; // 实际处理的文档数（有schema的）
    let skip = 0;
    const batchSize = 100;
    let hasMore = true;

    // 循环处理，每次100个
    while (hasMore) {
      try {
        // 查询 cssUpdateAt 不存在的文档，每次100个
        const documents = await this.documentModel
          .find({
            projectId: new Types.ObjectId(projectId),
            // fileName: 'confirmPaymentPage',
            // cssUpdateAt: { $exists: false },
          })
          .skip(skip)
          .limit(batchSize);

        if (documents.length === 0) {
          hasMore = false;
          break;
        }

        // 批量处理这批文档
        for (const doc of documents) {
          try {
            if (!doc.fileName) continue;

            processed++; // 统计实际处理的文档

            // 检查 schema 中是否有 CSS
            if (doc.schema) {
              try {
                // 使用独立的hash计算方法
                const { path, cssPath, tsxHash, cssHash, cleanedSchema } =
                  await calculateDocumentHashes(doc);
                console.log('tsxHash: ', tsxHash);

                // 更新文档，添加 CSS 相关字段
                await this.documentModel.updateOne(
                  { _id: doc._id },
                  {
                    $set: {
                      path,
                      cssPath,
                      hash: tsxHash,
                      cssHash: cssHash || null,
                      cssUpdateAt: Date.now(),
                    },
                  },
                );

                updated++;

                // 每处理10个输出一次进度
                if (updated % 10 === 0) {
                  console.log(`已更新文档 CSS: ${updated} 个`);
                }
              } catch (error) {
                console.error(`计算文档 ${doc.fileName} 的哈希失败`, error);
                // 不要直接返回，继续处理其他文档
                errors.push(
                  `计算文档 ${doc.fileName} 的哈希失败: ${error.message}`,
                );
              }
            }
          } catch (error) {
            errors.push(`更新文档 ${doc.fileName} 失败: ${error.message}`);
            console.error(`更新文档 ${doc.fileName} 失败`, error);
          }
        }

        skip += batchSize;
        console.log(
          `批次完成，已查询 ${skip} 个文档，实际处理 ${processed} 个，成功更新 ${updated} 个`,
        );
      } catch (error) {
        errors.push(`批次处理失败: ${error.message}`);
        console.error('批次处理失败', error);

        // 如果错误太多，中断处理
        if (errors.length > 10) {
          console.error('错误过多，中断批量更新');
          break;
        }
      }
    }

    console.log(
      `批量更新文档 CSS 完成: 总共查询 ${skip} 个文档，实际处理 ${processed} 个，成功更新 ${updated} 个，错误 ${errors.length} 个`,
    );
    return { updated, errors };
  }

  /**
   * 批量更新类型文件Hash
   */
  async updateITypeEntityHashes(
    projectId: string,
  ): Promise<{ updated: number; errors: string[] }> {
    const errors: string[] = [];
    let updated = 0;
    let processed = 0; // 实际处理的文档数（有schema的）
    let skip = 0;
    const batchSize = 100;
    let hasMore = true;

    // 循环处理，每次100个
    while (hasMore) {
      try {
        // 查询 cssUpdateAt 不存在的文档，每次100个
        const types = await this.typeModel
          .find({
            projectId: new Types.ObjectId(projectId),
            type: { $ne: 'folder' },
            // path: { $exists: false },
          })
          .skip(skip)
          .limit(batchSize);

        if (types.length === 0) {
          hasMore = false;
          break;
        }

        // 批量处理这批文档
        for (const typeItem of types) {
          try {
            if (!typeItem._id) continue;
            if (typeItem.type === 'folder') continue;

            processed++; // 统计实际处理的文档

            // 检查 schema 中是否有 CSS
            try {
              // 使用独立的hash计算方法
              const { path, size, hash } = await calculateTypeEntityHashes(
                typeItem,
              );

              console.log('hash: ', hash);

              // 更新文档，添加 CSS 相关字段
              await this.typeModel.updateOne(
                { _id: typeItem._id },
                {
                  $set: {
                    path,
                    size,
                    hash,
                  },
                },
              );

              updated++;

              // 每处理10个输出一次进度
              if (updated % 10 === 0) {
                console.log(`已更新类型: ${updated} 个`);
              }
            } catch (error) {
              console.error(`计算类型 ${typeItem.name} 的哈希失败`, error);
              // 不要直接返回，继续处理其他类型
              errors.push(
                `计算类型 ${typeItem.name} 的哈希失败: ${error.message}`,
              );
            }
          } catch (error) {
            errors.push(`更新类型 ${typeItem.name} 失败: ${error.message}`);
            console.error(`更新类型 ${typeItem.name} 失败`, error);
          }
        }

        skip += batchSize;
        console.log(
          `批次完成，已查询 ${skip} 个文档，实际处理 ${processed} 个，成功更新 ${updated} 个`,
        );
      } catch (error) {
        errors.push(`批次处理失败: ${error.message}`);
        console.error('批次处理失败', error);

        // 如果错误太多，中断处理
        if (errors.length > 10) {
          console.error('错误过多，中断批量更新');
          break;
        }
      }
    }

    console.log(
      `批量更新文档 CSS 完成: 总共查询 ${skip} 个文档，实际处理 ${processed} 个，成功更新 ${updated} 个，错误 ${errors.length} 个`,
    );
    return { updated, errors };
  }
}
